# version:1.1.2312.9221
import gxipy as gx
import time
from ctypes import *
from gxipy.gxidef import *
import numpy
import cv2  # 新增：用于显示图像
from gxipy.ImageFormatConvert import *

# 全局变量，用于存放采集的图像
display_frame = None

def get_best_valid_bits(pixel_format):
    valid_bits = DxValidBit.BIT0_7
    if pixel_format in (GxPixelFormatEntry.MONO8,
                        GxPixelFormatEntry.BAYER_GR8, GxPixelFormatEntry.BAYER_RG8,
                        GxPixelFormatEntry.BAYER_GB8, GxPixelFormatEntry.BAYER_BG8,
                        GxPixelFormatEntry.RGB8, GxPixelFormatEntry.BGR8,
                        GxPixelFormatEntry.R8, GxPixelFormatEntry.B8, GxPixelFormatEntry.G8):
        valid_bits = DxValidBit.BIT0_7
    elif pixel_format in (GxPixelFormatEntry.MONO10, GxPixelFormatEntry.MONO10_PACKED, GxPixelFormatEntry.MONO10_P,
                          GxPixelFormatEntry.BAYER_GR10, GxPixelFormatEntry.BAYER_RG10,
                          GxPixelFormatEntry.BAYER_GB10, GxPixelFormatEntry.BAYER_BG10,
                          GxPixelFormatEntry.BAYER_GR10_P, GxPixelFormatEntry.BAYER_RG10_P,
                          GxPixelFormatEntry.BAYER_GB10_P, GxPixelFormatEntry.BAYER_BG10_P,
                          GxPixelFormatEntry.BAYER_GR10_PACKED, GxPixelFormatEntry.BAYER_RG10_PACKED,
                          GxPixelFormatEntry.BAYER_GB10_PACKED, GxPixelFormatEntry.BAYER_BG10_PACKED):
        valid_bits = DxValidBit.BIT2_9
    elif pixel_format in (GxPixelFormatEntry.MONO12, GxPixelFormatEntry.MONO12_PACKED, GxPixelFormatEntry.MONO12_P,
                          GxPixelFormatEntry.BAYER_GR12, GxPixelFormatEntry.BAYER_RG12,
                          GxPixelFormatEntry.BAYER_GB12, GxPixelFormatEntry.BAYER_BG12,
                          GxPixelFormatEntry.BAYER_GR12_P, GxPixelFormatEntry.BAYER_RG12_P,
                          GxPixelFormatEntry.BAYER_GB12_P, GxPixelFormatEntry.BAYER_BG12_P,
                          GxPixelFormatEntry.BAYER_GR12_PACKED, GxPixelFormatEntry.BAYER_RG12_PACKED,
                          GxPixelFormatEntry.BAYER_GB12_PACKED, GxPixelFormatEntry.BAYER_BG12_PACKED):
        valid_bits = DxValidBit.BIT4_11
    elif pixel_format in (GxPixelFormatEntry.MONO14, GxPixelFormatEntry.MONO14_P,
                          GxPixelFormatEntry.BAYER_GR14, GxPixelFormatEntry.BAYER_RG14,
                          GxPixelFormatEntry.BAYER_GB14, GxPixelFormatEntry.BAYER_BG14,
                          GxPixelFormatEntry.BAYER_GR14_P, GxPixelFormatEntry.BAYER_RG14_P,
                          GxPixelFormatEntry.BAYER_GB14_P, GxPixelFormatEntry.BAYER_BG14_P):
        valid_bits = DxValidBit.BIT6_13
    elif pixel_format in (GxPixelFormatEntry.MONO16,
                          GxPixelFormatEntry.BAYER_GR16, GxPixelFormatEntry.BAYER_RG16,
                          GxPixelFormatEntry.BAYER_GB16, GxPixelFormatEntry.BAYER_BG16):
        valid_bits = DxValidBit.BIT8_15
    return valid_bits

def convert_to_special_pixel_format(raw_image, pixel_format):
    image_convert.set_dest_format(pixel_format)
    valid_bits = get_best_valid_bits(raw_image.get_pixel_format())
    image_convert.set_valid_bits(valid_bits)

    # 创建输出图像缓冲区
    buffer_out_size = image_convert.get_buffer_size_for_conversion(raw_image)
    output_image_array = (c_ubyte * buffer_out_size)()
    output_image = addressof(output_image_array)

    # 转换到目标pixel_format
    image_convert.convert(raw_image, output_image, buffer_out_size, False)
    if output_image is None:
        print('Pixel format conversion failed')
        return

    return output_image_array, buffer_out_size

def capture_callback_color(raw_image):
    global display_frame
    # 打印采集图像的帧ID、高度和宽度
    print("Timestamp: %.2f | Frame ID: %d   Height: %d   Width: %d" %
          (time.time(), raw_image.get_frame_id(), raw_image.get_height(), raw_image.get_width()))

    if raw_image.get_pixel_format() != GxPixelFormatEntry.RGB8:
        rgb_image_array, rgb_image_buffer_length = convert_to_special_pixel_format(raw_image, GxPixelFormatEntry.RGB8)
        if rgb_image_array is None:
            return
        numpy_image = numpy.frombuffer(rgb_image_array, dtype=numpy.ubyte, count=rgb_image_buffer_length)\
                            .reshape(raw_image.frame_data.height, raw_image.frame_data.width, 3)
    else:
        numpy_image = raw_image.get_numpy_array()

    if numpy_image is None:
        print('Failed to get numpy array from RGBImage')
        return

    # 转换为 OpenCV 格式（BGR）
    numpy_image_bgr = cv2.cvtColor(numpy_image, cv2.COLOR_RGB2BGR)
    display_frame = numpy_image_bgr

def capture_callback_mono(raw_image):
    global display_frame
    # 打印采集图像的帧ID、高度和宽度
    print("Frame ID: %d   Height: %d   Width: %d" %
          (raw_image.get_frame_id(), raw_image.get_height(), raw_image.get_width()))

    if raw_image.get_pixel_format() not in (GxPixelFormatEntry.MONO8, GxPixelFormatEntry.R8, GxPixelFormatEntry.B8, GxPixelFormatEntry.G8):
        mono_image_array, mono_image_buffer_length = convert_to_special_pixel_format(raw_image, GxPixelFormatEntry.MONO8)
        if mono_image_array is None:
            return
        numpy_image = numpy.frombuffer(mono_image_array, dtype=numpy.ubyte, count=mono_image_buffer_length)\
                            .reshape(raw_image.frame_data.height, raw_image.frame_data.width)
    else:
        numpy_image = raw_image.get_numpy_array()

    if numpy_image is None:
        print('Failed to get numpy array from RawImage')
        return

    display_frame = numpy_image

def main():
    global display_frame
    print("")
    print("-------------------------------------------------------------")
    print("Sample to show how to continuously acquire image by callback "
          "and display image via OpenCV window.")
    print("-------------------------------------------------------------")
    print("")
    print("Initializing......")
    print("")
    cam_index = 1

    # 创建设备管理器
    device_manager = gx.DeviceManager()
    dev_num, dev_info_list = device_manager.update_all_device_list()
    if dev_num == 0:
        print("Number of enumerated devices is 0")
        return

    # 打开第一个设备（注意：设备索引从1开始）
    cam = device_manager.open_device_by_index(cam_index)
    remote_device_feature = cam.get_remote_device_feature_control()

    # 获取图像格式转换对象
    global image_convert
    image_convert = device_manager.create_image_format_convert()

    # 设置曝光
    exposure_time_feature = remote_device_feature.get_float_feature("ExposureTime")
    exposure_time_feature.set(70000.0)

    # 设置触发模式和触发源（根据设备类型区分）
    trigger_mode_feature = remote_device_feature.get_enum_feature("TriggerMode")
    if dev_info_list[0].get("device_class") == gx.GxDeviceClassList.USB2:
        trigger_mode_feature.set("On")
    else:
        trigger_mode_feature.set("On")
        trigger_source_feature = remote_device_feature.get_enum_feature("TriggerSource")
        trigger_source_feature.set("Software")

    # 获取数据流
    data_stream = cam.data_stream[0]

    # 注册采集回调函数，判断图像类型并注册相应回调
    pixel_format_value, pixel_format_str = remote_device_feature.get_enum_feature("PixelFormat").get()
    if gx.Utility.is_gray(pixel_format_value):
        data_stream.register_capture_callback(capture_callback_mono)
    else:
        data_stream.register_capture_callback(capture_callback_color)

    # 开始数据采集
    cam.stream_on()
    print('<Start acquisition>')

    # 获取软件触发命令对象（仅当使用软件触发时有效）
    trigger_software_command_feature = remote_device_feature.get_command_feature("TriggerSoftware")

    # 创建 OpenCV 窗口以显示图像
    cv2.namedWindow("Camera", cv2.WINDOW_NORMAL)

    try:
        # 进入循环：不断触发采集并显示最新回调到的图像，按 'q' 键退出视频
        while True:
            # 发送触发命令，触发软件采集（根据具体设备条件可能需不断触发）
            trigger_software_command_feature.send_command()

            # 如果有新的图像，则显示
            if display_frame is not None:
                cv2.imshow("Camera", display_frame)
                print(time.time())


            # 等待 1 毫秒检测键盘输入
            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                print("退出视频采集")
                break

            # 可适当控制触发帧率，例如 sleep 10 毫秒（根据实际需要调整）
            time.sleep(0.01)
    except KeyboardInterrupt:
        print("手动中断")
    finally:
        # 停止采集，注销回调并关闭设备
        cam.stream_off()
        print('<Stop acquisition>')
        data_stream.unregister_capture_callback()
        cam.close_device()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    main()
